using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Stride.Core.CompilerServices.Common;

namespace Stride.Core.CompilerServices.Generators;
[Generator]
public class ModuleInitializerGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {

        var provider = context.SyntaxProvider.ForAttributeWithMetadataName(WellKnownReferences.ModuleInitializerAttributeName,
                    (node, transform) => node is MethodDeclarationSyntax { ReturnType: PredefinedTypeSyntax ret } mds
                    && ret.Keyword.IsKind(SyntaxKind.VoidKeyword)
                    && mds.ParameterList.Parameters is []
                    && mds.Arity == 0
                    && mds.Modifiers.Any(t => t.IsKind(SyntaxKind.StaticKeyword)),
                    (ctx, transform) =>
                    {
                        if (ctx.TargetSymbol is not IMethodSymbol method)
                            return null;

                        int order = 0;
                        var moduleInitializerType = WellKnownReferences.ModuleInitializerAttribute(ctx.SemanticModel.Compilation)!;
                        if (method.TryGetAttribute(moduleInitializerType, out var attributeData))
                        {
                            if (attributeData is { AttributeConstructor.Parameters: [{ Name: "order" }, ..], ConstructorArguments: [{ Value: int orderNumber }, ..] })
                            {
                                order = orderNumber;
                            }
                        }

                        var className = method.ContainingType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) + "." + method.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);

                        return new OrderedModuleInitializerMethod(order, className);
                    }
                );
        var provider2 = provider.Collect();

        context.RegisterSourceOutput(provider2, Generate);
    }

    private void Generate(SourceProductionContext context, ImmutableArray<OrderedModuleInitializerMethod?> array)
    {
        var code = new StringBuilder();
        if (array.Length == 0)
            return;
        foreach (var module in array.OrderBy(x => x?.Order))
        {
            if (module is not null)
            {
                code.AppendLine($"\t\t\t{module.InvocationString}();");
            }
        }
        var outputClass = @$"
namespace Stride.Core.CompilerServices.AutoGenerated.ModuleInitializer
{{
    file static class ModuleInitializer
    {{
        [System.Runtime.CompilerServices.ModuleInitializer]
        public static void Initialize()
        {{
{code}        }}
    }}
}}
";
        context.AddSource("ModuleInitializer.cs", outputClass);
    }

    public record OrderedModuleInitializerMethod(int Order, string InvocationString);
}
